// This kernel perform vectorized floating-point addition/multiplication
// to demonstrate how parallel processing can accelerate computation
// 2020.06.17 by wangdong@bjtu.edu.cn
//#include "ap_int.h"
//#include <stdio.h> 

typedef unsigned int  uint;

#define VEC_SIZE   16

// coding style 1
//typedef ap_uint<VEC_SIZE*32> float_vec;
//typedef  hls::vector<float, VEC_SIZE> float_vec;
 // coding style 2
typedef struct {
   float data[VEC_SIZE];
} float_vec;

//--------------------- Baseline -----------------------//

extern "C" {
void vecAdd(
                const float_vec *A1_in,
                const float_vec *A2_in,
                float_vec *B_out,
                uint data_num
                )
{
#pragma HLS INTERFACE m_axi port = A1_in  offset = slave bundle = gmem0 // group-0
#pragma HLS INTERFACE m_axi port = A2_in  offset = slave bundle = gmem1 // group-1
#pragma HLS INTERFACE m_axi port = B_out  offset = slave bundle = gmem2 // group-2

	float tmp;
	
	for(uint i=0; i<data_num; i++){
	#pragma HLS pipeline II=2
		for(unsigned char k=0; k<VEC_SIZE; k++){
		#pragma HLS unroll
			tmp = 0.0;
			for(unsigned char j=0; j<VEC_SIZE; j++){
			#pragma HLS unroll
				tmp += (A1_in[i].data[j] + A2_in[i].data[k]);
			}
			B_out[i].data[k] = tmp;
		}
	}

}
}

//---------------------- FMAX Optimized ---------------------------//
//force adding register in HLS
template<typename T>
T reg(T d){
    #pragma HLS PIPELINE II=1
    #pragma HLS LATENCY max=1 min=1
    #pragma HLS INLINE off
    return d;
}
/*
extern "C" {
void vecAdd(
                const float_vec *A1_in,
                const float_vec *A2_in,
                float_vec *B_out,
                uint data_num
                )
{
#pragma HLS INTERFACE m_axi port = A1_in  offset = slave bundle = gmem0 // group-0
#pragma HLS INTERFACE m_axi port = A2_in  offset = slave bundle = gmem1 // group-1
#pragma HLS INTERFACE m_axi port = B_out  offset = slave bundle = gmem2 // group-2
#pragma HLS expression_balance

	float tmp1, tmp2;
	float A2_in_reg1, A2_in_reg2; // used to reduce fan-out
	
	for(uint i=0; i<data_num; i++){
	#pragma HLS pipeline II=1
		for(unsigned char k=0; k<VEC_SIZE; k++){
		#pragma HLS unroll
			tmp1 = 0.0;
			tmp2 = 0.0;
			// copy A2 to two registers
			A2_in_reg1 = reg<float>(A2_in[i].data[k]);
			A2_in_reg2 = reg<float>(A2_in[i].data[k]);
			// div into two parts
			for(unsigned char j=0; j<VEC_SIZE/2; j++){
			#pragma HLS unroll
				tmp1 += (A1_in[i].data[j] + A2_in_reg1);
			}
			for(unsigned char jj=0; jj<VEC_SIZE/2; jj++){
			#pragma HLS unroll
				tmp2 += (A1_in[i].data[VEC_SIZE/2 + jj] + A2_in_reg2);
			}
			B_out[i].data[k] = tmp1 + tmp2;
		}
	}

}
}
*/
